昨天做到雙方都出卡後,增加 turn 數
現在在做 每到 3 turn 換一局 round + 1
昨天做到這邊
def handle_cast({:play_card, player, card}, game) do
game =
game
|> play_card_for(player, card)
|> maybe_end_turn()
{:noreply, game}
end
defp play_card_for(game, player, card) do
data =
game
|> Map.get(player)
|> then(&Map.merge(&1, %{hand: &1.hand -- [card], desk: &1.desk ++ [card]}))
Map.replace(game, player, data)
end
defp maybe_end_turn(%{guest: guest, host: host} = game) do
if length(guest.desk) == length(host.desk) do
Map.merge(game, %{turn: game.turn + 1})
else
game
end
end
也許這個可以不要跟 turn 同一個方法
來寫一個 maybe_end_round 好了
defp maybe_end_round(%{turn: turn} = game) when turn > 3 do
Map.merge(game, %{turn: 1, round: game.round + 1})
end
defp maybe_end_round(game), do: game
這是什麼巫術,怎麼有 do:
然後沒有 end
先從為什麼要寫兩個 maybe_end_round 開始
還不習慣這個的時候可以會寫出
defp maybe_end_round(%{turn: turn} = game) do
if turn > 3 do
Map.merge(game, %{turn: 1, round: game.round + 1})
else
game
end
end
我們先利用 when 把簡單的判斷拉到 pattern matching 方法層面的 guards
這樣子這個方法除了變數 match 之外,when 的條件也要符合,但 when 只能使用基本的方法 詳見 Guards 文件
拉出來之後方法可以變成兩個
defp maybe_end_round(%{turn: turn} = game) when turn > 3 do
Map.merge(game, %{turn: 1, round: game.round + 1})
end
defp maybe_end_round(game) do
game
end
再把只回傳 game 沒有做別的事的小方法改寫成一行,用 do:
至於為什麼可以這樣寫,牽扯到 macro ,
這個比較進階,我也還在摸索,但在這次應該是不會用到,
我們先記得有這個可以簡化單行方法就好
把他加在 handle_cast :play_card 後面
def handle_cast({:play_card, player, card}, game) do
game =
game
|> play_card_for(player, card)
|> maybe_end_turn()
|> maybe_end_round()
{:noreply, game}
end
在 iex 試試看
iex(1)> import Game
Game
iex(2)> {:ok, pid} = start
{:ok, #PID<0.113.0>}
iex(3)> play_card pid, :host, 3
:ok
iex(4)> play_card pid, :guest, 4
:ok
iex(5)> play_card pid, :host, 1
:ok
iex(6)> play_card pid, :guest, 2
:ok
iex(7)> play_card pid, :host, 1
:ok
iex(8)> play_card pid, :guest, 5
:ok
iex(9)> status pid
%Game{
guest: %{desk: [4, 2, 5], hand: [1, 1, 2, 3, 3, 6, :turn, :turn], wins: 0},
host: %{desk: [3, 1, 1], hand: [2, 2, 3, 4, 5, 6, :turn, :turn], wins: 0},
round: 2,
turn: 1
}
成功
另外還有就是,我們可能要限制一下,同一個人不能連續出牌,要等雙方都出完,完成一回合才能再往下,
這點也許後面會改,但是我們先把它做好來。
def handle_cast({:play_card, :host, _card}, %{host: host, guest: guest} = game)
when length(host.desk) > length(guest.desk),
do: {:noreply, game}
def handle_cast({:play_card, :guest, _card}, %{host: host, guest: guest} = game)
when length(host.desk) < length(guest.desk),
do: {:noreply, game}
這次我寫在原本的 handle_cast :play_card 前面來攔截,
假如 host 出牌,可是他牌桌上的牌數已經比對方多了,這代表他是連續出,所以就忽略這次動作,反之亦然
雖然我覺得沒有比起前面收成方法疊再一起,但先這樣吧
iex 試試看
iex(1)> import Game
Game
iex(2)> {:ok, pid} = start
{:ok, #PID<0.113.0>}
iex(3)> play_card pid, :host, 1
:ok
iex(4)> play_card pid, :host, 1
:ok
iex(5)> status pid
%Game{
guest: %{desk: [], hand: [1, 1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
host: %{desk: [1], hand: [1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
round: 1,
turn: 1
}
完成拉
我們有出卡,有回合,好像可以來算個輸贏,
今天先撇開特殊卡片,假如只出數字卡的情況。
在換 round 的時候,來算一下上一 round 的結果,
誰贏就在誰的 wins 裡面 + 1
defp maybe_end_round(%{turn: turn} = game) when turn > 3 do
game
|> Map.merge(%{turn: 1, round: game.round + 1})
|> compare_score(score(:host, game), score(:guest, game))
end
這裡加一個 compare_score/3 收 game, host分數, guest分數
分數的部分也另外寫一個 socre 方法算
# 這兩個方法用 when 裡面來判斷誰的點數大
defp compare_score(game, host_score, guest_score) when host_score > guest_score,
do: add_wins(game, :host)
defp compare_score(game, host_score, guest_score) when host_score < guest_score,
do: add_wins(game, :guest)
defp add_wins(game, player) do
# 這個方法幫獲勝者加一勝
player_data =
game
|> Map.get(player)
|> then(&Map.merge(&1, %{wins: &1.wins + 1}))
Map.replace(game, player, player_data)
end
defp score(player, %{round: round} = game) do
range_start = (round - 1) * 3
range = range_start..(range_start + 2)
# range 是依照回合要從桌上拿的卡片範圍
# 1 局 就是從 desk 裡面利用 slice 方法拿 0..2 位置的卡片
# 2 局 就是 3..5 位置的卡片,依此類推
game
|> Map.get(player)
|> Map.get(:desk)
|> Enum.slice(range)
|> Enum.sum() #最後用 sum 計算總和,回傳分數
end
好像離可以玩的狀態,好拉在 iex 可以玩的狀態不遠了。
再撐一下,這個核心的地方完成之後,我們做介面會比較放心。